// Package bootstrap db 数据库连接
package bootstrap

import (
	"bookkeeping-gin/app/model"
	"bookkeeping-gin/global"
	"go.uber.org/zap"
	"gopkg.in/natefinch/lumberjack.v2"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"io"
	"log"
	"os"
	"time"
)

func InitDB() *gorm.DB {
	var db *gorm.DB
	switch global.App.Config.Database.Driver {
	case "mysql":
		db = initMysql()
	default:
		db = initMysql()
	}
	global.App.DB = db
	return db
}

func initMysql() *gorm.DB {
	dbConfig := global.App.Config.Database

	if dbConfig.Database == "" {
		return nil
	}

	dsn := dbConfig.Username + ":" + dbConfig.Password + "@tcp(" + dbConfig.Host + ":" + dbConfig.Port + ")/" + dbConfig.Database + "?charset=" + dbConfig.Charset + "&parseTime=True&loc=Local"
	mysqlConfig := mysql.Config{
		DSN:                       dsn,   // DSN data source name
		DefaultStringSize:         256,   // Default size for strings
		DisableDatetimePrecision:  true,  // Disable datetime precision, which not supported before MySQL 5.6
		DontSupportRenameIndex:    true,  // Don't rename index
		DontSupportRenameColumn:   true,  // Don't rename column
		SkipInitializeWithVersion: false, // Skip initializing database with version
	}

	db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		Logger:                                   getGormLogger(),
	})

	if err != nil {
		global.App.Log.Error("mysql connect failed, err：", zap.Any("err", err))
		log.Fatal("mysql connect failed, err：", err)
		return nil
	}

	sqlDB, _ := db.DB()
	sqlDB.SetMaxIdleConns(dbConfig.MaxIdleConns)
	sqlDB.SetMaxOpenConns(dbConfig.MaxOpenConns)

	// 数据库
	initMySqlTables(db)
	return db
}

// 数据库表初始化
func initMySqlTables(db *gorm.DB) {
	err := db.AutoMigrate(
		model.User{},
		model.Tag{},
	)
	if err != nil {
		log.Println("initMySqlTables err:", err)
		global.App.Log.Error("migrate table failed", zap.Any("err", err))
		os.Exit(0)
	}
}

func getGormLogger() logger.Interface {
	var logMode logger.LogLevel

	switch global.App.Config.Database.LogMode {
	case "silent":
		logMode = logger.Silent
	case "error":
		logMode = logger.Error
	case "warn":
		logMode = logger.Warn
	case "info":
		logMode = logger.Info
	default:
		logMode = logger.Info
	}
	return logger.New(getGormLogWriter(), logger.Config{
		SlowThreshold:             200 * time.Millisecond,                          // Slow SQL threshold
		LogLevel:                  logMode,                                         // Log level
		IgnoreRecordNotFoundError: false,                                           // Ignore ErrRecordNotFound error for logger
		Colorful:                  !global.App.Config.Database.EnableFileLogWriter, // Colorful
	})
}

func getGormLogWriter() logger.Writer {
	var writer io.Writer

	// enable file log writer ?
	if global.App.Config.Database.EnableFileLogWriter {
		// custom writer
		writer = &lumberjack.Logger{
			Filename: global.App.Config.Log.RootDir + "/" + global.App.Config.Database.LogFilename,
			MaxSize:  global.App.Config.Log.MaxSize,
			MaxAge:   global.App.Config.Log.MaxAge,
			Compress: global.App.Config.Log.Compress,
		}
	} else {
		// default
		writer = os.Stdout
	}

	return log.New(writer, "\r\n", log.LstdFlags)
}
